home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Freeware / Griffith 0.9.8 / griffith-0.9.8-win32.exe / {app} / lib / dbupgrade.py < prev    next >
Text File  |  2008-11-17  |  19KB  |  460 lines

  1. # -*- coding: UTF-8 -*-
  2.  
  3. __revision__ = '$Id: dbupgrade.py 1056 2008-11-16 23:29:08Z piotrek $'
  4.  
  5. # Copyright (c) 2005-2008 Vasco Nunes, Piotr O┼╝arowski
  6. #
  7. # This program is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 2 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. # GNU Library General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program; if not, write to the Free Software
  19. # 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  20.  
  21. # You may use and distribute this software under the terms of the
  22. # GNU General Public License, version 2 or later
  23.  
  24. from sqlalchemy import *
  25. import os.path
  26. import gutils
  27. import gtk
  28.  
  29.  
  30. def upgrade_database(self, version):
  31.     """Create new db or update existing one to current format"""
  32.     if version == 0:
  33.         self.Configuration.mapper.mapped_table.create()
  34.         self.Configuration.mapper.mapped_table.insert().execute(param='version', value=self.version)
  35.         self.Volume.mapper.mapped_table.create()
  36.         self.Collection.mapper.mapped_table.create()
  37.         self.Medium.mapper.mapped_table.create()
  38.         self.Medium.mapper.mapped_table.insert().execute(name='DVD')
  39.         self.Medium.mapper.mapped_table.insert().execute(name='DVD-R')
  40.         self.Medium.mapper.mapped_table.insert().execute(name='DVD-RW')
  41.         self.Medium.mapper.mapped_table.insert().execute(name='DVD+R')
  42.         self.Medium.mapper.mapped_table.insert().execute(name='DVD+RW')
  43.         self.Medium.mapper.mapped_table.insert().execute(name='DVD-RAM')
  44.         self.Medium.mapper.mapped_table.insert().execute(name='CD')
  45.         self.Medium.mapper.mapped_table.insert().execute(name='CD-RW')
  46.         self.Medium.mapper.mapped_table.insert().execute(name='VCD')
  47.         self.Medium.mapper.mapped_table.insert().execute(name='SVCD')
  48.         self.Medium.mapper.mapped_table.insert().execute(name='VHS')
  49.         self.Medium.mapper.mapped_table.insert().execute(name='BETACAM')
  50.         self.Medium.mapper.mapped_table.insert().execute(name='LaserDisc')
  51.         self.ACodec.mapper.mapped_table.create()
  52.         self.ACodec.mapper.mapped_table.insert().execute(name='AC-3 Dolby audio')
  53.         self.ACodec.mapper.mapped_table.insert().execute(name='OGG')
  54.         self.ACodec.mapper.mapped_table.insert().execute(name='MP3')
  55.         self.ACodec.mapper.mapped_table.insert().execute(name='MPEG-1')
  56.         self.ACodec.mapper.mapped_table.insert().execute(name='MPEG-2')
  57.         self.ACodec.mapper.mapped_table.insert().execute(name='AAC')
  58.         self.ACodec.mapper.mapped_table.insert().execute(name='Windows Media Audio')
  59.         self.VCodec.mapper.mapped_table.create()
  60.         self.VCodec.mapper.mapped_table.insert().execute(name='MPEG-1')
  61.         self.VCodec.mapper.mapped_table.insert().execute(name='MPEG-2')
  62.         self.VCodec.mapper.mapped_table.insert().execute(name='XviD')
  63.         self.VCodec.mapper.mapped_table.insert().execute(name='DivX')
  64.         self.VCodec.mapper.mapped_table.insert().execute(name='H.264')
  65.         self.VCodec.mapper.mapped_table.insert().execute(name='RealVideo')
  66.         self.VCodec.mapper.mapped_table.insert().execute(name='QuickTime')
  67.         self.VCodec.mapper.mapped_table.insert().execute(name='Windows Media Video')
  68.         self.AChannel.mapper.mapped_table.create()
  69.         self.AChannel.mapper.mapped_table.insert().execute(name='mono')
  70.         self.AChannel.mapper.mapped_table.insert().execute(name='stereo')
  71.         self.AChannel.mapper.mapped_table.insert().execute(name='5.1')
  72.         self.AChannel.mapper.mapped_table.insert().execute(name='7.1')
  73.         self.SubFormat.mapper.mapped_table.create()
  74.         self.SubFormat.mapper.mapped_table.insert().execute(name='DVD VOB')
  75.         self.SubFormat.mapper.mapped_table.insert().execute(name='MPL2 (.txt)')
  76.         self.SubFormat.mapper.mapped_table.insert().execute(name='MicroDVD (.sub)')
  77.         self.SubFormat.mapper.mapped_table.insert().execute(name='SubRip (.srt)')
  78.         self.SubFormat.mapper.mapped_table.insert().execute(name='SubViewer2 (.sub)')
  79.         self.SubFormat.mapper.mapped_table.insert().execute(name='Sub Station Alpha (.ssa)')
  80.         self.SubFormat.mapper.mapped_table.insert().execute(name='Advanced Sub Station Alpha (.ssa)')
  81.         self.Person.mapper.mapped_table.create()
  82.         self.Movie.mapper.mapped_table.create()
  83.         self.Loan.mapper.mapped_table.create()
  84.         self.Lang.mapper.mapped_table.create()
  85.         self.Lang.mapper.mapped_table.insert().execute(name=_('Brazilian Portuguese'))
  86.         self.Lang.mapper.mapped_table.insert().execute(name=_('Bulgarian'))
  87.         self.Lang.mapper.mapped_table.insert().execute(name=_('Catalan'))
  88.         self.Lang.mapper.mapped_table.insert().execute(name=_('Czech'))
  89.         self.Lang.mapper.mapped_table.insert().execute(name=_('Danish'))
  90.         self.Lang.mapper.mapped_table.insert().execute(name=_('Dutch'))
  91.         self.Lang.mapper.mapped_table.insert().execute(name=_('English'))
  92.         self.Lang.mapper.mapped_table.insert().execute(name=_('Estonian'))
  93.         self.Lang.mapper.mapped_table.insert().execute(name=_('French'))
  94.         self.Lang.mapper.mapped_table.insert().execute(name=_('German'))
  95.         self.Lang.mapper.mapped_table.insert().execute(name=_('Greek'))
  96.         self.Lang.mapper.mapped_table.insert().execute(name=_('Hungarian'))
  97.         self.Lang.mapper.mapped_table.insert().execute(name=_('Indonesian'))
  98.         self.Lang.mapper.mapped_table.insert().execute(name=_('Italian'))
  99.         self.Lang.mapper.mapped_table.insert().execute(name=_('Japanese'))
  100.         self.Lang.mapper.mapped_table.insert().execute(name=_('Korean'))
  101.         self.Lang.mapper.mapped_table.insert().execute(name=_('Norwegian Bokmal'))
  102.         self.Lang.mapper.mapped_table.insert().execute(name=_('Occitan'))
  103.         self.Lang.mapper.mapped_table.insert().execute(name=_('Pashto'))
  104.         self.Lang.mapper.mapped_table.insert().execute(name=_('Polish'))
  105.         self.Lang.mapper.mapped_table.insert().execute(name=_('Portuguese'))
  106.         self.Lang.mapper.mapped_table.insert().execute(name=_('Russian'))
  107.         self.Lang.mapper.mapped_table.insert().execute(name=_('Simplified Chinese'))
  108.         self.Lang.mapper.mapped_table.insert().execute(name=_('Slovak'))
  109.         self.Lang.mapper.mapped_table.insert().execute(name=_('Spanish'))
  110.         self.Lang.mapper.mapped_table.insert().execute(name=_('Swedish'))
  111.         self.Lang.mapper.mapped_table.insert().execute(name=_('Turkish'))
  112.         self.MovieLang.mapper.mapped_table.create()
  113.         self.Tag.mapper.mapped_table.create()
  114.         self.Tag.mapper.mapped_table.insert().execute(name=_('Favourite'))
  115.         self.MovieTag.mapper.mapped_table.create()
  116.         #self.metadata.commit()
  117.         return True # upgrade process finished
  118.     if version == 1: # fix changes between v1 and v2
  119.         version+=1
  120.         self.metadata.engine.execute("UPDATE loans SET return_date = '2007-01-01' WHERE return_date='None';")
  121.         db_version = self.Configuration.get_by(param='version')
  122.         db_version.value = version
  123.         db_version.update()
  124.         db_version.flush()
  125.     #if version == 2:    # fix changes between v2 and v3
  126.     #    version+=1
  127.     #    self.Configuration.get_by(param='version').value = version
  128.     return True
  129.  
  130.  
  131. # ---------------------------------------------------
  132. # for Griffith <= 0.6.2 compatibility
  133. # ---------------------------------------------------
  134.  
  135. def convert_from_old_db(self, source_file, destination_file):    #{{{
  136.     print 'Converting old database - it can take several minutes...'
  137.     gutils.info(self,_("Griffith will now convert your database to the new format. This can take several minutes if you have a large database."))
  138.     from sqlalchemy.orm import clear_mappers
  139.     from sql import GriffithSQL
  140.     from gutils import digits_only
  141.     import os
  142.  
  143.     if not os.path.isfile(source_file):
  144.         return False
  145.     if open(source_file).readline()[:47] == '** This file contains an SQLite 2.1 database **':
  146.         try:
  147.             import sqlite
  148.             from sqlite import DatabaseError
  149.         except ImportError:
  150.             print 'Old DB conversion: please install pysqlite legacy (v1.0)'
  151.             gutils.warning(self,_("Old DB conversion: please install pysqlite legacy (v1.0)"))
  152.             return False
  153.     else:
  154.         try:    # Python 2.5
  155.             from sqlite3 import dbapi2 as sqlite
  156.             from sqlite3.dbapi2 import DatabaseError
  157.         except ImportError: # Python < 2.5 - try to use pysqlite2
  158.             from pysqlite2 import dbapi2 as sqlite
  159.             from pysqlite2.dbapi2 import DatabaseError
  160.  
  161.     if os.path.isfile(destination_file):
  162.         # rename destination_file if it already exist
  163.         i = 1
  164.         while True:
  165.             if os.path.isfile("%s_%s" % (destination_file, i)):
  166.                 i += 1
  167.             else:
  168.                 break
  169.         os.rename(destination_file, "%s_%s" % (destination_file, i))
  170.  
  171.     try:
  172.         old_db = sqlite.connect(source_file)
  173.     except DatabaseError, e:
  174.         if str(e) == 'file is encrypted or is not a database':
  175.             print 'Your database is most probably in wrong SQLite format, please convert it to SQLite3:'
  176.             print '$ sqlite ~/.griffith/griffith.gri .dump | sqlite3 ~/.griffith/griffith.gri3'
  177.             print '$ mv ~/.griffith/griffith.gri{,2}'
  178.             print '$ mv ~/.griffith/griffith.gri{3,}'
  179.             print 'or install pysqlite in version 1.0'
  180.             gutils.warning(self,_("Your database is most probably in SQLite2 format, please convert it to SQLite3"))
  181.         else:
  182.             raise
  183.         return False
  184.  
  185.     old_cursor = old_db.cursor()
  186.  
  187.     # fix old database
  188.     old_cursor.execute("UPDATE movies SET media = '1' WHERE media = 'DVD';")
  189.     old_cursor.execute("UPDATE movies SET media = '2' WHERE media = 'DVD-R';")
  190.     old_cursor.execute("UPDATE movies SET media = '3' WHERE media = 'DVD-RW';")
  191.     old_cursor.execute("UPDATE movies SET media = '4' WHERE media = 'DVD+R';")
  192.     old_cursor.execute("UPDATE movies SET media = '5' WHERE media = 'DVD+RW';")
  193.     old_cursor.execute("UPDATE movies SET media = '6' WHERE media = 'DVD-RAM';")
  194.     old_cursor.execute("UPDATE movies SET media = '7' WHERE media = 'DivX';")
  195.     old_cursor.execute("UPDATE movies SET media = '7' WHERE media = 'DIVX';")
  196.     old_cursor.execute("UPDATE movies SET media = '7' WHERE media = 'XviD';")
  197.     old_cursor.execute("UPDATE movies SET media = '7' WHERE media = 'XVID';")
  198.     old_cursor.execute("UPDATE movies SET media = '7' WHERE media = 'WMV';")
  199.     old_cursor.execute("UPDATE movies SET media = '9' WHERE media = 'VCD';")
  200.     old_cursor.execute("UPDATE movies SET media = '10' WHERE media = 'SVCD';     ")
  201.     old_cursor.execute("UPDATE movies SET media = '11' WHERE media = 'VHS';")
  202.     old_cursor.execute("UPDATE movies SET media = '12' WHERE media = 'BETACAM';")
  203.     old_cursor.execute("UPDATE movies SET collection_id=0 WHERE collection_id<1")
  204.     old_cursor.execute("UPDATE movies SET volume_id=0 WHERE volume_id<1")
  205.     old_cursor.execute("UPDATE movies SET color=NULL WHERE color<1 OR color='' OR color>3")
  206.     old_cursor.execute("UPDATE movies SET condition=NULL WHERE condition<0 OR condition='' OR condition>5")
  207.     old_cursor.execute("UPDATE movies SET layers=NULL WHERE layers<0 OR layers='' OR layers>4")
  208.     old_cursor.execute("UPDATE movies SET region=NULL WHERE region='' OR region=2 OR region<0 OR region>8")
  209.     old_cursor.execute("UPDATE movies SET year=NULL WHERE year<1900 or year>2007")
  210.     old_cursor.execute("UPDATE movies SET rating = 0 WHERE rating NOT IN (0,1,2,3,4,5,6,7,8,9,10);") # rating>10 doesn't work with some DB
  211.     old_cursor.execute("UPDATE movies SET runtime = NULL WHERE runtime > 10000;") # remove strings
  212.     old_cursor.execute("UPDATE loans SET return_date=NULL WHERE return_date=''")
  213.     old_cursor.execute("UPDATE loans SET return_date=NULL WHERE return_date='None'")
  214.     old_cursor.execute("DELETE FROM loans WHERE date='' OR date ISNULL")
  215.     old_cursor.execute("DELETE FROM volumes WHERE name = ''")
  216.     old_cursor.execute("DELETE FROM volumes WHERE name = 'None'")
  217.     old_cursor.execute("DELETE FROM collections WHERE name = ''")
  218.     old_cursor.execute("DELETE FROM collections WHERE name = 'None'")
  219.     old_cursor.execute("DELETE FROM languages WHERE name = ''")
  220.     
  221.     self.config.set('type','sqlite', section='database')
  222.     self.config.set('file', 'griffith.db', section='database')
  223.     self.config['posters'] = 'posters'
  224.     self.config.set('color', 0, section='defaults')
  225.     self.config.set('condition', 0, section='defaults')
  226.     self.config.set('layers', 0, section='defaults')
  227.     self.config.set('media', 0, section='defaults')
  228.     self.config.set('region', 0, section='defaults')
  229.     self.config.set('vcodec', 0, section='defaults')
  230.     self.locations['posters'] = os.path.join(self.locations['home'], 'posters')
  231.     new_db = GriffithSQL(self.config, self.debug, self.locations['home'])
  232.  
  233.     # collections
  234.     collection_mapper = {'':None, u'':None, 0:None, '0':None, -1:None, '-1':None}
  235.     old_cursor.execute("SELECT id, name FROM collections;") # loaned status will be set later - buggy databases :-(
  236.     for i in old_cursor.fetchall():
  237.         o = new_db.Collection(name=i[1])
  238.         try:
  239.             o.save(); o.flush()
  240.         except Exception, e:
  241.             self.debug.show(str(e))
  242.             continue
  243.         collection_mapper[i[0]] = o.collection_id
  244.     
  245.     # volumes
  246.     volume_mapper = {'':None, u'':None, 0:None, '0':None, -1:None, '-1':None}
  247.     old_cursor.execute("SELECT id, name FROM volumes;") # loaned status will be set later - buggy databases :-(
  248.     for i in old_cursor.fetchall():
  249.         o = new_db.Volume(name=i[1])
  250.         try:
  251.             o.save(); o.flush()
  252.         except Exception, e:
  253.             self.debug.show(str(e))
  254.             continue
  255.         volume_mapper[i[0]] = o.volume_id
  256.  
  257.     # people
  258.     person_mapper = {}
  259.     old_cursor.execute("SELECT id, name, email, phone FROM people;")
  260.     for i in old_cursor.fetchall():
  261.         o = new_db.Person(name=i[1], email=i[2], phone=i[3])
  262.         try:
  263.             o.save(); o.flush()
  264.         except Exception, e:
  265.             self.debug.show(str(e))
  266.             continue
  267.         person_mapper[i[0]] = o.person_id
  268.     
  269.     # languages
  270.     language_mapper = {'':None, u'':None, 0:None, '0':None, -1:None, '-1':None}
  271.     old_cursor.execute("SELECT id, name FROM languages;")
  272.     for i in old_cursor.fetchall():
  273.         o = new_db.Lang.get_by(name=i[1])
  274.         if o is not None:
  275.             language_mapper[i[0]] = o.lang_id
  276.         else:
  277.             o = new_db.Lang(name=i[1])
  278.             try:
  279.                 o.save(); o.flush()
  280.             except Exception, e:
  281.                 self.debug.show(str(e))
  282.                 continue
  283.             language_mapper[i[0]] = o.lang_id
  284.  
  285.     # media
  286.     medium_mapper = {'':None, u'':None, 0:None, '0':None, -1:None, '-1':None}
  287.     old_cursor.execute("SELECT id, name FROM media;")
  288.     for i in old_cursor.fetchall():
  289.         o = new_db.Medium.get_by(name=i[1])
  290.         if o is not None:
  291.             medium_mapper[i[0]] = o.medium_id
  292.         else:
  293.             o = new_db.Medium(name=i[1])
  294.             try:
  295.                 o.save(); o.flush()
  296.             except Exception, e:
  297.                 self.debug.show(str(e))
  298.                 continue
  299.             medium_mapper[i[0]] = o.medium_id
  300.     
  301.     # tags
  302.     tag_mapper = {}
  303.     old_cursor.execute("SELECT id, name FROM tags;")
  304.     for i in old_cursor.fetchall():
  305.         o = new_db.Tag.get_by(name=i[1])
  306.         if o is not None:
  307.             tag_mapper[i[0]] = o.tag_id
  308.         else:
  309.             o = new_db.Tag(name=i[1])
  310.             try:
  311.                 o.save(); o.flush()
  312.             except Exception, e:
  313.                 self.debug.show(str(e))
  314.                 continue
  315.             tag_mapper[i[0]] = o.tag_id
  316.     
  317.     # movies
  318.     movie_mapper = {}
  319.     old_cursor.execute("""
  320.         SELECT id, volume_id, collection_id, original_title, title, director,
  321.             number, image, plot, country, year, runtime, classification,
  322.             genre, studio, site, imdb, actors, trailer, rating, loaned,
  323.             media, num_media, obs, seen, region, condition, color, layers
  324.         FROM movies ORDER BY number;""")
  325.     for i in old_cursor.fetchall():
  326.         o = new_db.Movie()
  327.         o.number = digits_only(i[6])
  328.         if volume_mapper.has_key(i[1]):
  329.             o.volume_id = volume_mapper[i[1]]
  330.         if collection_mapper.has_key(i[2]):
  331.             o.collection_id = collection_mapper[i[2]]
  332.         o.o_title = i[3][:255]
  333.         o.title = i[4][:255]
  334.         o.director = i[5][:255]
  335.         o.image = i[7][:128]
  336.         o.plot = i[8]
  337.         o.country = i[9][:128]
  338.         o.year = digits_only(i[10])
  339.         o.runtime = digits_only(i[11])
  340.         o.classification = i[12][:128]
  341.         o.genre = i[13][:128]
  342.         o.studio = i[14][:128]
  343.         o.o_site = i[15][:255]
  344.         o.site = i[16][:255]
  345.         o.cast = i[17]
  346.         o.trailer = i[18][:255]
  347.         o.rating = digits_only(i[19])
  348.         #o.loaned = bool(i[20]) # updated later
  349.         if medium_mapper.has_key(int(i[21])):
  350.             o.medium_id = medium_mapper[int(i[21])]
  351.         o.media_num = digits_only(i[22])
  352.         o.notes = i[23]
  353.         o.seen = bool(i[24])
  354.         o.region = digits_only(i[25])
  355.         o.cond = digits_only(i[26], 5)
  356.         o.color = digits_only(i[27], 3)
  357.         o.layers = digits_only(i[28], 4)
  358.         
  359.         try:
  360.             o.save(); o.flush()
  361.         except Exception, e:
  362.             self.debug.show(str(e))
  363.             continue
  364.         movie_mapper[i[0]] = o.movie_id
  365.  
  366.     # movie tag
  367.     old_cursor.execute("SELECT movie_id, tag_id FROM movie_tag WHERE movie_id IN (SELECT id FROM movies);")
  368.     for i in old_cursor.fetchall():
  369.         o = new_db.MovieTag.get_by(movie_id=movie_mapper[i[0]], tag_id=tag_mapper[i[1]])
  370.         if o is None:
  371.             m = new_db.Movie.get_by(movie_id=movie_mapper[i[0]])
  372.             t = new_db.Tag.get_by(tag_id=tag_mapper[i[1]])
  373.             t.save()
  374.             m.tags.append(t)
  375.             try:
  376.                 m.save(); m.flush()
  377.             except Exception, e:
  378.                 self.debug.show(str(e))
  379.                 continue
  380.     
  381.     # movie lang
  382.     old_cursor.execute("SELECT movie_id, lang_id, type FROM movie_lang WHERE movie_id IN (SELECT id FROM movies);")
  383.     for i in old_cursor.fetchall():
  384.         o = new_db.MovieLang.get_by(movie_id=movie_mapper[i[0]], lang_id=language_mapper[i[1]], type=i[2])
  385.         if o is None:
  386.             m = new_db.Movie.get_by(movie_id=movie_mapper[i[0]])
  387.             l = new_db.MovieLang(lang_id=language_mapper[i[1]], type=i[2])
  388.             l.save()
  389.             m.languages.append(l)
  390.             try:
  391.                 m.save(); m.flush()
  392.             except Exception, e:
  393.                 self.debug.show(str(e))
  394.                 continue
  395.  
  396.     # loans
  397.     old_cursor.execute("SELECT person_id, movie_id, volume_id, collection_id, date, return_date FROM loans;")
  398.     for i in old_cursor.fetchall():
  399.         vol = col = None
  400.         not_returned = i[5] is None
  401.  
  402.         if int(i[2]) > 0:
  403.             try:
  404.                 vol = new_db.Volume.get_by(volume_id=volume_mapper[i[2]])
  405.             except Exception, e:
  406.                 self.debug.show(str(e))
  407.                 continue
  408.         if int(i[3]) > 0:
  409.             try:
  410.                 col = new_db.Collection.get_by(collection_id=collection_mapper[i[3]])
  411.             except Exception, e:
  412.                 self.debug.show(str(e))
  413.                 continue
  414.         if int(i[1]) == 0:
  415.             if vol is not None and len(vol.movies)>0:
  416.                 m = vol.movies[0]
  417.             elif col is not None and len(col.movies)>0:
  418.                 m = col.movies[0]
  419.             else:
  420.                 self.debug.show("Cannot find associated movie for this loan (%s)" % i)
  421.                 continue
  422.         else:
  423.             try:
  424.                 m = new_db.Movie.get_by(movie_id=movie_mapper[i[1]])
  425.             except Exception, e:
  426.                 self.debug.show(str(e))
  427.                 continue
  428.         
  429.         l = new_db.Loan()
  430.         l.person_id = person_mapper[i[0]]
  431.         l.date = str(i[4])[:10]
  432.         if not_returned:
  433.             m.loaned = True
  434.             l.return_date = None
  435.         else:
  436.             l.return_date = str(i[5])[:10]
  437.         
  438.         # update volume / collection status
  439.         if int(i[2]) > 0:
  440.             l.volume_id = volume_mapper[i[2]]
  441.             if not_returned:
  442.                 vol.loaned = True
  443.                 vol.save()
  444.         if int(i[3]) > 0:
  445.             l.collection_id = collection_mapper[i[3]]
  446.             if not_returned:
  447.                 col.loaned = True
  448.                 col.save()
  449.         l.save();
  450.         m.loans.append(l)
  451.         try:
  452.             m.flush()
  453.         except Exception, e:
  454.             self.debug.show(str(e))
  455.             continue
  456.     clear_mappers()
  457.     return True
  458. #}}}
  459. # vim: fdm=marker
  460.